//______________________________________________________________________________
//
// File:...... FModManager.cpp
//
// Author:.... Robert Martinez (Bobby)
//
// Date:...... 12/29/2007 12:16:36 AM
//
// Purpose:.... function definitions for fMod wrapper class
//______________________________________________________________________________

#include "stdafx.h"
#include "FModManager.h"

#include <assert.h>

FModManager FModManager::m_pInstance;	//	initialization of single class instance 

FModManager::FModManager(void) : m_pSystem(NULL) , m_hWnd(0), m_dwCurrentTime(0), m_dwPreviousTime(0), m_dTimeAccumulator(0) 
{
}

FModManager::~FModManager(void) 
{
}

FModManager *FModManager::GetInstance( void )
{
	return &m_pInstance;	//	return instance of the class
}

bool FModManager::InitFModManager( HWND hWnd, int nMaxChannels, FMOD_INITFLAGS  unFlags, void *vExtraDriverData ) 
{
	//	Paranoid error check
	if (!hWnd || nMaxChannels < 0 || unFlags < 0) return false;

	//	get handle to window 
	m_hWnd = hWnd;

	//	used to catch fMod-specific error codes
	FMOD_RESULT result;

	//	Create the main system object.
	if ((result = FMOD::System_Create(&m_pSystem)) != FMOD_OK)
	{
		FMODERR( result );
	}

	// Initialize FMOD.
	if ((result = m_pSystem->init( nMaxChannels, unFlags, vExtraDriverData ) ) != FMOD_OK)
	{
		FMODERR( result );
	}

	//	init timers from garbage collection
	m_dwCurrentTime = GetTickCount();
	m_dwPreviousTime = GetTickCount();
	m_bIsSpectrumAnalysisOn = FALSE;
	// Set the spectrum analysis DSP to NULL

	return true;
}

int FModManager::LoadSound(const char *szFilename,int _SoundType, FMOD_MODE unMode ) 
{
	if( !m_pSystem ) return -1;

	if( !szFilename ) return -1;

	int nHandle = -1;	//	an interger that is returned to the user so they can reference this sound again
	int counter = 0;	//	used to keep track of where in a loop we we're indexing in

	//	used to catch fMod-specific error codes
	FMOD_RESULT result;

	vector<tSoundInfo>::iterator vIter = m_SoundList.begin();	

	while( vIter != m_SoundList.end() ) 
	{
		if( _stricmp( (*vIter).filename, szFilename ) == 0 ) 
		{
			(*vIter).ref++;
			nHandle = counter;
			break;
			//	only hole in this logic:
			//	what if a sound is loaded that has a different FMOD_MODE?
			//	has to be taken into account
		}
		vIter++;
		counter++;
	}

	if( nHandle != -1) return nHandle;		//	We've found our handle!

	//	re-initing indexer
	counter = 0;


	//	if one of the already used sounds is done with, reuse that spot
	//	NOTE:
	//	(only use this if I can reset each object
	while( vIter != m_SoundList.end() ) 
	{
		if( (*vIter).ref == 0 ) 
		{
			nHandle = counter;
			break;
		}
		vIter++;
		counter++;
	}

	//	the sound has not been loaded before,
	//	so a new one must be made based on this file
	tSoundInfo newSound;

	if( nHandle == -1 ) 
	{
		//	copy name into object
		strncpy_s( newSound.filename, _MAX_PATH, szFilename, strlen( szFilename ) );

		//	first instance of this sound
		newSound.ref = 1;

		//	set flags to the sound
		newSound.unSoundFormat |= unMode;
		newSound.SoundType = _SoundType;

		// FMOD_DEFAULT uses the defaults.  These are the same as FMOD_LOOP_OFF | FMOD_2D | FMOD_HARDWARE
		if( ( result = m_pSystem->createSound( newSound.filename, newSound.unSoundFormat, 0, &newSound.fmSound ) ) != FMOD_OK )
		{
			FMODERR( result );
		}

		//	push new allocated sound onto vector
		m_SoundList.push_back( newSound );

		return (int)m_SoundList.size() - 1;

	}
	else
	{
		//	release anything still in m_SoundList[ nHandle ];
		//	then copy above 

	}

	return nHandle;
}

bool FModManager::UnloadSound( int nID )
{
	if( !m_pSystem ) return false;

	//	check that ID is in range
	assert( nID > -1 && nID < (int)m_SoundList.size() && "ID is out of range" );

	//	used to catch fMod-specific error codes
	FMOD_RESULT result;
	bool bOutcome = false;

	if( m_SoundList[nID].ref == 0 || ( result = m_SoundList[nID].fmSound->release() ) == FMOD_OK ) 
	{
		ZeroMemory(m_SoundList[nID].filename, _MAX_PATH);
		m_SoundList[nID].fmSound = NULL;
		m_SoundList[nID].ref = 0;
		bOutcome = true;
	}
	else
		FMODERR( result );

	return bOutcome;
}

bool FModManager::PlaySound( int nID ) 
{
	if( !m_pSystem ) return false;

	//	check that ID is in range
	assert( nID > -1 && nID < (int)m_SoundList.size() && "ID is out of range" );

	//	Address of a channel handle pointer that receives the newly playing channel
	FMOD::Channel *channel = NULL;
	//	used to catch fMod-specific error codes
	FMOD_RESULT result;

	//Hack
	//Todo: Find out why this isn't working properly -- that is why the handles are being lost and the heap corrupted.

	if( ( result = m_pSystem->playSound(FMOD_CHANNEL_FREE, m_SoundList[ nID ].fmSound , false, &channel) ) != FMOD_OK || result == FMOD_ERR_INVALID_HANDLE) 
	{
		//FMODERR( result );

		return FALSE;
	}

	//end Hack


	//	hold on to channel pointer for later use
	m_SoundList[ nID ].m_SoundChannels.push_back( channel );

	//	return success
	return true;
}

bool FModManager::ResetSound( int nID )
{
	if( !m_pSystem ) return false;

	//	check that ID is in range
	assert( nID > -1 && nID < (int)m_SoundList.size() && "ID is out of range" );

	//	used to catch fMod-specific error codes
	FMOD_RESULT result;
	bool bOutcome = false;

	//	iterate all the channels in this sound
	vector<FMOD::Channel*>::iterator iter = m_SoundList[nID].m_SoundChannels.begin();

	while( iter != m_SoundList[nID].m_SoundChannels.end() ) 
	{
		if( ( result = (*iter)->isPlaying( &bOutcome ) ) == FMOD_OK ) 
		{
			(*iter)->setPosition( 0,  FMOD_TIMEUNIT_MS);
		}
		iter++;
	}

	//	return the outcome of this function
	return bOutcome;
}

bool FModManager::StopSound( int nID )
{
	if( !m_pSystem ) return false;

	//	check that ID is in range
	assert( nID > -1 && nID < (int)m_SoundList.size() && "ID is out of range" );

	//	used to catch fMod-specific error codes
	FMOD_RESULT result;
	bool bOutcome = false;

	//	iterate all the channels in this sound
	vector<FMOD::Channel*>::iterator iter = m_SoundList[nID].m_SoundChannels.begin();

	while( iter != m_SoundList[nID].m_SoundChannels.end() )
	{
		if( ( result = (*iter)->isPlaying( &bOutcome ) ) == FMOD_OK ) 
		{
			(*iter)->stop( );
		}
		iter++;

	}

	//	return the outcome of this function
	return bOutcome;
}

bool FModManager::SetVolume( int nID, float fVolume )
{
	if( !m_pSystem ) return false;

	//	check that ID is in range
	assert( nID > -1 && nID < (int)m_SoundList.size() && "ID is out of range" );

	//	used to catch fMod-specific error codes
	FMOD_RESULT result;
	bool bOutcome = false;

	//	iterate all the channels in this sound
	vector<FMOD::Channel*>::iterator iter = m_SoundList[nID].m_SoundChannels.begin();

	while( iter != m_SoundList[nID].m_SoundChannels.end() ) 
	{
		if( ( result = (*iter)->isPlaying( &bOutcome ) ) == FMOD_OK ) 
		{
			(*iter)->setVolume( fVolume );
		}
		iter++;
	}

	//	return the outcome of this function
	return bOutcome;
}

bool FModManager::SetFrequency( int nID, float fFreq )
{
	if( !m_pSystem ) return false;

	//	check that ID is in range
	assert( nID > -1 && nID < (int)m_SoundList.size() && "ID is out of range" );

	//	used to catch fMod-specific error codes
	FMOD_RESULT result;
	bool bOutcome = false;

	//	iterate all the channels in this sound
	vector<FMOD::Channel*>::iterator iter = m_SoundList[nID].m_SoundChannels.begin();

	while( iter != m_SoundList[nID].m_SoundChannels.end() )
	{
		if( ( result = (*iter)->isPlaying( &bOutcome ) ) == FMOD_OK ) 
		{
			(*iter)->setFrequency( fFreq );
		}
		iter++;
	}

	//	return the outcome of this function
	return bOutcome;
}

bool FModManager::SetPan( int nID, float fPan ) 
{
	if( !m_pSystem ) return false;

	//	check that ID is in range
	assert( nID > -1 && nID < (int)m_SoundList.size() && "ID is out of range" );

	//	used to catch fMod-specific error codes
	FMOD_RESULT result;
	bool bOutcome = false;

	//	iterate all the channels in this sound
	vector<FMOD::Channel*>::iterator iter = m_SoundList[nID].m_SoundChannels.begin();

	while( iter != m_SoundList[nID].m_SoundChannels.end() )
	{
		if( ( result = (*iter)->isPlaying( &bOutcome ) ) == FMOD_OK ) 
		{
			(*iter)->setPan( fPan );
		}
		iter++;
	}

	//	return the outcome of this function
	return bOutcome;
}

bool FModManager::IsSoundPlaying( int nID ) {

	if( !m_pSystem ) return false;

	//	check that ID is in range
	assert( nID > -1 && nID < (int)m_SoundList.size() && "ID is out of range" );

	//	used to catch fMod-specific error codes
	FMOD_RESULT result;
	bool bOutcome = false;

	//	iterate all the channels in this sound
	vector<FMOD::Channel*>::iterator iter = m_SoundList[nID].m_SoundChannels.begin();

	while( iter != m_SoundList[nID].m_SoundChannels.end() ) 
	{
		if( ( result = (*iter)->isPlaying( &bOutcome ) ) == FMOD_OK ) 
		{
			break;
		}
		iter++;
	}

	//	return the outcome of this function
	return bOutcome;
}

bool FModManager::Update( void ) 
{
	//	used to catch fMod-specific error codes
	FMOD_RESULT result;

	if( ( result = m_pSystem->update() ) != FMOD_OK)
	{
		FMODERR( result );
	}

	//	TODO: Add garbage collector here

	m_dwPreviousTime = m_dwCurrentTime;

	m_dwCurrentTime = GetTickCount();

	//	how much time has past since our last call to update
	double dDeltaTime = static_cast<double>( (m_dwCurrentTime - m_dwPreviousTime) / 1000.0f);

	m_dTimeAccumulator += dDeltaTime;

	//	collect garbage fMod handles every few seconds
	if( m_dTimeAccumulator >= TIMER_COUNT ) 
	{
		ReduceHandleCount( );
		//	reset accumulation buffer
		m_dTimeAccumulator -= TIMER_COUNT;
	}

	//	Success!
	return true;
}

void FModManager::ReduceHandleCount( void ) 
{
	//	used to catch fMod-specific error codes
	FMOD_RESULT result;
	bool bOutcome = false;
	vector<tSoundInfo>::iterator vecIter = m_SoundList.begin();

	//	for all sound file loaded, check to see on their channel handles
	while( vecIter != m_SoundList.end() )
	{
		vector<FMOD::Channel *>::iterator listIter = (*vecIter).m_SoundChannels.begin();

		while( listIter != (*vecIter).m_SoundChannels.end() ) 
		{

			bOutcome = false;
			//	if the Handle is invalid, get rid of it
			if( ( result = (*listIter)->isPlaying( &bOutcome ) ) != FMOD_OK || result == FMOD_ERR_INVALID_HANDLE) 
			{
				listIter = (*vecIter).m_SoundChannels.erase( listIter );


			}
			else	//	otherwise, keep moving along the list
				listIter++;


		}	//	End of m_SoundChannels loop

		vecIter++;	//	move to next list of handles

	}	//	End of m_SoundList loop 
}

bool FModManager::ShutdownFModManager( void ) 
{
	//	iterate through all sounds
	vector<tSoundInfo>::iterator vIter = m_SoundList.begin();
	//	keep trak of where we are in the vector
	int counter = 0;

	for( counter = 0; counter < (int)m_SoundList.size(); counter++) 
	{
		//	go through vector, releasing all sounds
		if( UnloadSound( counter ) ) { }
	}

	//	clear out vector
	m_SoundList.clear();

	//	all done
	return true;
}

void FModManager::SetAllVol_Music(float _vol)
{

	int counter = 0;	//	used to keep track of where in a loop we we're indexing in

	vector<tSoundInfo>::iterator vIter = m_SoundList.begin();	

	while( vIter != m_SoundList.end() ) 
	{
		if(m_SoundList[counter].SoundType == MUSIC)
			SetVolume(counter, _vol);
		counter++;
		vIter++;
	}
	m_fCurrentMusicLevel = _vol;
}

void FModManager::SetAllVol_SFX(float _vol)
{

	int counter = 0;	//	used to keep track of where in a loop we we're indexing in

	vector<tSoundInfo>::iterator vIter = m_SoundList.begin();	

	while( vIter != m_SoundList.end() ) 
	{
		if(m_SoundList[counter].SoundType == SOUND_EFFECTS)
			SetVolume(counter, _vol);
		counter++;
		vIter++;
	}
	m_fCurrentSFXLevel = _vol;
}


void FModManager::GetCurrentSpectrum(float * f_spectrum,int n_Channeloffset, int n_spectrumsize)
{
	m_pSystem->getSpectrum(f_spectrum, n_spectrumsize, n_Channeloffset, FMOD_DSP_FFT_WINDOW_BLACKMANHARRIS);
}

void FModManager::GetNormalizedSpectrum(float * f_spectrum, int n_Channeloffset, int n_SpectrumSize)
{
	GetCurrentSpectrum(f_spectrum, n_Channeloffset, n_SpectrumSize);
	float f_max = 0.0f;
	for(int i = 0; i < n_SpectrumSize; i++)
	{
		if(f_spectrum[i] > f_max)
			f_max = f_spectrum[i];

	}

	for(int i = 0; i < n_SpectrumSize; i++)
	{
		f_spectrum[i] *= (1/f_max);
	}
}


void FModManager::ReversePlayingSample(int nID)
{
	if( !m_pSystem ) return;

	int counter = 0;

	float _freq = 0;
	if(nID == -1)
	{

		vector<tSoundInfo>::iterator vIter = m_SoundList.begin();	
		vector<FMOD::Channel*>::iterator iter = m_SoundList[counter].m_SoundChannels.begin();


		while( vIter != m_SoundList.end() ) 
		{

			while( iter != m_SoundList[counter].m_SoundChannels.end() )
			{
				(*iter)->getFrequency(&_freq);
				(*iter)->setFrequency( _freq * -1);
				iter++;

			}
			counter ++;
			vIter++;

		}


		return;
	}
	else
	{

		//	check that ID is in range
		assert( nID >= 0 && nID < (int)m_SoundList.size() && "ID is out of range" );

		//	used to catch fMod-specific error codes
		//FMOD_RESULT result;
		bool bOutcome = false;

		//	iterate all the channels in this sound
		vector<FMOD::Channel*>::iterator iter = m_SoundList[nID].m_SoundChannels.begin();

		while( iter != m_SoundList[nID].m_SoundChannels.end() )
		{
			(*iter)->getFrequency(&_freq);
			(*iter)->setFrequency( _freq * -1);
			iter++;
		}
	}


}


char * FModManager::GetFileName(int nID)
{
	if( !m_pSystem ) return false;

	//	check that ID is in range
	assert( nID > -1 && nID < (int)m_SoundList.size() && "ID is out of range" );

	//	used to catch fMod-specific error codes
	//	FMOD_RESULT result;
	bool bOutcome = false;


	m_SoundList[nID].fmSound->getName(tempName, 64);

	return tempName;



}

//
///*
//
///*===============================================================================================
//PlayList Example
//Copyright (c), Firelight Technologies Pty, Ltd 2004-2008.
//
//This example shows how to load a playlist and play the sounds in a playlist.
//===============================================================================================*/
///*
//#include <windows.h>
//#include <stdio.h>
//#include <conio.h>
//
//#include "../../api/inc/fmod.hpp"
//#include "../../api/inc/fmod_errors.h"
//
//void ERRCHECK(FMOD_RESULT result)
//{
//	if (result != FMOD_OK)
//	{
//		printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
//		exit(-1);
//	}
//}
//
//
//int main(int argc, char *argv[])
//{
//	FMOD::System     *system   = 0;
//	FMOD::Sound      *playlist = 0;
//	FMOD::Sound      *sound    = 0;
//	FMOD::Channel    *channel  = 0;
//	FMOD_TAG          tag;
//	FMOD_RESULT       result;
//	FMOD_SOUND_TYPE   soundtype;
//	bool              isplaylist = false;
//	char             *title = NULL;
//	int               count = 0;
//	int               key;
//	unsigned int      version;
//	char              file[128];
//
//	/*
//	Create a System object and initialize.
//	*/
//	result = FMOD::System_Create(&system);
//	ERRCHECK(result);
//
//	result = system->getVersion(&version);
//	ERRCHECK(result);
//
//	if (version < FMOD_VERSION)
//	{
//		printf("Error!  You are using an old version of FMOD %08x.  This program requires %08x\n", version, FMOD_VERSION);
//		return 0;
//	}
//
//	result = system->init(32, FMOD_INIT_NORMAL, 0);
//	ERRCHECK(result);
//
//	result = system->createSound("../media/playlist.m3u", FMOD_DEFAULT, 0, &playlist);
//	ERRCHECK(result);
//
//	result = playlist->getFormat(&soundtype, 0, 0, 0);
//	ERRCHECK(result);
//
//	isplaylist = (soundtype == FMOD_SOUND_TYPE_PLAYLIST);
//
//	printf("===================================================================\n");
//	printf("PlayList Example.  Copyright (c) Firelight Technologies 2004-2008.\n");
//	printf("===================================================================\n");
//	printf("\n");
//	printf("Press 'n'     to play next sound in playlist\n");
//	printf("Press 'space' to pause/unpause current sound\n");
//	printf("Press 'Esc'   to quit\n");
//	printf("\n");
//
//	if (isplaylist)
//	{
//		printf("PLAYLIST loaded.\n");
//		/*
//		Get the first song in the playlist, create the sound and then play it.
//		*/
//		result = playlist->getTag("FILE", count, &tag);
//		ERRCHECK(result);
//
//		sprintf(file, "../media/%s", (char *)tag.data);
//
//		result = system->createSound(file, FMOD_DEFAULT, 0, &sound);
//		ERRCHECK(result);
//
//		result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
//		ERRCHECK(result);
//
//		playlist->getTag("TITLE", count, &tag);
//		title = (char *)tag.data;
//
//		count++;
//	}
//	else
//	{
//		printf("SOUND loaded.\n");
//
//		/*
//		This is just a normal sound, so just play it.
//		*/
//		sound = playlist;
//
//		result = sound->setMode(FMOD_LOOP_NORMAL);
//		ERRCHECK(result);
//
//		result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
//		ERRCHECK(result);
//	}
//
//	printf("\n");
//
//	/*
//	Main loop.
//	*/
//	do
//	{
//		bool  isplaying = false;
//
//		if (channel && isplaylist)
//		{
//			/*
//			When sound has finished playing, play the next sound in the playlist
//			*/
//
//			channel->isPlaying(&isplaying);
//			if (!isplaying)
//			{
//				if (sound)
//				{
//					sound->release();
//
//					sound = NULL;
//				}
//
//				result = playlist->getTag("FILE", count, &tag);
//				if (result != FMOD_OK)
//				{
//					count = 0;
//				}
//				else
//				{
//					printf("playing next song in playlist...\n");
//
//					sprintf(file, "../media/%s", (char *)tag.data);
//
//					result = system->createSound(file, FMOD_DEFAULT, 0, &sound);
//					ERRCHECK(result);
//
//					result = system->playSound(FMOD_CHANNEL_FREE, sound, false, &channel);
//					ERRCHECK(result);
//
//					playlist->getTag("TITLE", count, &tag);
//					title = (char *)tag.data;
//
//					count++;
//				}
//			}
//		}
//
//
//		if (kbhit())
//		{
//			key = getch();
//
//			switch (key)
//			{
//			case 'n' :
//				{
//					/*
//					Play the next song in the playlist
//					*/
//					if (channel && isplaylist)
//					{
//						channel->stop();
//					}
//
//					break;
//				}
//			case ' ' :
//				{
//					if (channel)
//					{
//						bool paused;
//
//						channel->getPaused(&paused);
//						channel->setPaused(!paused);
//					}
//				}
//			}
//		}
//
//		system->update();
//
//		{
//			unsigned int ms = 0;
//			unsigned int lenms = 0;
//			bool         paused = 0;
//
//			if (channel)
//			{
//				if (sound)
//				{
//					result = sound->getLength(&lenms, FMOD_TIMEUNIT_MS);
//					if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HANDLE) && (result != FMOD_ERR_CHANNEL_STOLEN))
//					{
//						ERRCHECK(result);
//					}
//				}
//
//				result = channel->getPaused(&paused);
//				if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HANDLE) && (result != FMOD_ERR_CHANNEL_STOLEN))
//				{
//					ERRCHECK(result);
//				}
//
//				result = channel->getPosition(&ms, FMOD_TIMEUNIT_MS);
//				if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HANDLE) && (result != FMOD_ERR_CHANNEL_STOLEN))
//				{
//					ERRCHECK(result);
//				}
//			}
//
//			printf("Time %02d:%02d:%02d/%02d:%02d:%02d : %s : %s\r", ms / 1000 / 60, ms / 1000 % 60, ms / 10 % 100, lenms / 1000 / 60, lenms / 1000 % 60, lenms / 10 % 100, paused ? "Paused " : "Playing ", title);
//
//		}
//
//		Sleep(10);
//
//	} while (key != 27);
//
//	printf("\n");
//
//	/*
//	Shut down
//	*/
//	if (sound)
//	{
//		result = sound->release();
//		ERRCHECK(result);
//	}
//	if (isplaylist)
//	{
//		result = playlist->release();
//		ERRCHECK(result);
//	}
//	result = system->close();
//	ERRCHECK(result);
//	result = system->release();
//	ERRCHECK(result);
//
//	return 0;
//}
//
//
//*/